Controlled Components in React JS
Controlled Components in React JS are form elements whose values are controlled by React state. They allow developers to manage user input, validate data, update fields dynamically, and handle form submissions efficiently. Controlled components commonly use the useState Hook, the value or checked prop, and event handlers such as onChange.
Course Link: JustAcademy React JS Training Course
Demo Link: Register for React JS Course Demo
1. Learning Objectives
After completing these notes, you will be able to:
- Understand the concept of controlled components.
- Learn how React state controls form input values.
- Use
useState to manage user input.
- Handle text inputs, checkboxes, radio buttons, dropdowns, and textareas.
- Manage multiple form fields using a single state object.
- Validate input values and display error messages.
- Handle form submission and reset form fields.
- Understand the difference between controlled and uncontrolled components.
- Build a complete controlled registration form.
2. What Are Controlled Components?
A controlled component is a form element whose current value is managed by React state. The state determines what appears in the input, and an event handler updates the state when the user changes the input.
For example, when a user types their name into a controlled input, the onChange handler updates the state. React then renders the input using the updated state value.
Basic Example
import { useState } from "react";
function NameForm() {
const [name, setName] = useState("");
return (
<div>
<label htmlFor="name">Enter Your Name:</label>
<input
id="name"
type="text"
value={name}
onChange={(event) => setName(event.target.value)}
/>
<p>Your Name: {name}</p>
</div>
);
}
export default NameForm;
How It Works
useState("") initializes the name state with an empty string.
value={name} makes React state control the input value.
onChange runs when the user edits the input.
event.target.value reads the latest text entered by the user.
setName() updates the state.
- React re-renders the component with the updated value.
3. How Controlled Components Work
Controlled components follow a simple cycle: React state provides the current value, the user changes the input, the event handler updates the state, and React renders the new value.
Controlled Component Flow
React State
↓
Input Receives Value
↓
User Changes Input
↓
onChange Event Runs
↓
State Updates
↓
React Re-renders Input
This approach makes React state the source of truth for the current input value.
4. Important Properties and Events
| Property/Event |
Purpose |
Example |
useState |
Stores the current input value. |
useState("") |
value |
Controls the value of text inputs, textareas, and dropdowns. |
value={name} |
checked |
Controls whether a checkbox or radio button is selected. |
checked={accepted} |
onChange |
Handles user changes and updates state. |
onChange={handleChange} |
event.target.value |
Reads the current value of most input elements. |
event.target.value |
event.target.checked |
Reads the selected state of a checkbox or radio input. |
event.target.checked |
onSubmit |
Handles form submission. |
onSubmit={handleSubmit} |
preventDefault() |
Prevents the browser's default submission behavior. |
event.preventDefault() |
5. Controlled Text Input
A controlled text input uses a state variable and the value prop to manage the text entered by a user.
import { useState } from "react";
function TextInput() {
const [username, setUsername] = useState("");
return (
<div>
<label htmlFor="username">Username:</label>
<input
id="username"
type="text"
value={username}
onChange={(event) => setUsername(event.target.value)}
/>
<p>Username: {username}</p>
</div>
);
}
export default TextInput;
Explanation: The input always displays the value stored in username. When the user types, setUsername() updates the state and React displays the updated text.
6. Controlled Email Input
Controlled email inputs are useful for registration forms, login pages, contact forms, and newsletter subscriptions.
import { useState } from "react";
function EmailForm() {
const [email, setEmail] = useState("");
return (
<form>
<label htmlFor="email">Email Address:</label>
<input
id="email"
type="email"
value={email}
onChange={(event) => setEmail(event.target.value)}
/>
<p>Entered Email: {email}</p>
</form>
);
}
export default EmailForm;
The type="email" attribute provides an appropriate input type and browser validation behavior. If your application needs custom validation, you can validate the state value as well.
7. Controlled Password Input
A controlled password input stores the entered password in React state. The browser masks the displayed characters because the input type is set to password.
import { useState } from "react";
function PasswordForm() {
const [password, setPassword] = useState("");
return (
<div>
<label htmlFor="password">Password:</label>
<input
id="password"
type="password"
value={password}
onChange={(event) => setPassword(event.target.value)}
/>
<p>Password Length: {password.length}</p>
</div>
);
}
export default PasswordForm;
Security note: Do not display, log, or unnecessarily store passwords. Real authentication should be handled securely by a backend.
8. Controlled Textarea
The textarea element collects longer text such as feedback, comments, descriptions, and messages. A controlled textarea uses the value and onChange props.
import { useState } from "react";
function FeedbackForm() {
const [feedback, setFeedback] = useState("");
return (
<div>
<label htmlFor="feedback">Your Feedback:</label>
<textarea
id="feedback"
value={feedback}
onChange={(event) => setFeedback(event.target.value)}
rows={4}
placeholder="Write your feedback here"
/>
<p>Characters: {feedback.length}</p>
</div>
);
}
export default FeedbackForm;
Unlike traditional HTML, React controlled textareas use the value prop to specify their current text.
9. Controlled Dropdown Using select
A controlled dropdown allows React to manage which option is selected. Use value on the select element and update its state with onChange.
import { useState } from "react";
function CourseSelector() {
const [course, setCourse] = useState("");
return (
<div>
<label htmlFor="course">Select a Course:</label>
<select
id="course"
value={course}
onChange={(event) => setCourse(event.target.value)}
>
<option value="">Choose a course</option>
<option value="react">React JS</option>
<option value="flutter">Flutter</option>
<option value="figma">Figma UI/UX</option>
</select>
<p>Selected Course: {course || "None"}</p>
</div>
);
}
export default CourseSelector;
The value of the select element determines the selected option. In a controlled dropdown, set the selected value on the parent element instead of using the HTML selected attribute on individual options.
10. Controlled Checkbox
A checkbox is used for choices that can be independently selected, such as accepting terms and conditions or subscribing to a newsletter. Use checked to control its state.
import { useState } from "react";
function TermsCheckbox() {
const [accepted, setAccepted] = useState(false);
return (
<div>
<label>
<input
type="checkbox"
checked={accepted}
onChange={(event) => setAccepted(event.target.checked)}
/>
I accept the terms and conditions.
</label>
<p>Status: {accepted ? "Accepted" : "Not accepted"}</p>
</div>
);
}
export default TermsCheckbox;
Important: Use event.target.checked to read whether the checkbox is selected. Do not use value to control whether it is checked.
11. Controlled Radio Buttons
Radio buttons allow users to choose one option from a group. Each option shares the same name, and its checked prop depends on the current state.
import { useState } from "react";
function LearningMode() {
const [mode, setMode] = useState("online");
return (
<div>
<p>Select Learning Mode:</p>
<label>
<input
type="radio"
name="mode"
value="online"
checked={mode === "online"}
onChange={(event) => setMode(event.target.value)}
/>
Online
</label>
<label>
<input
type="radio"
name="mode"
value="classroom"
checked={mode === "classroom"}
onChange={(event) => setMode(event.target.value)}
/>
Classroom
</label>
<p>Selected Mode: {mode}</p>
</div>
);
}
export default LearningMode;
When the user selects a radio button, React updates mode. The matching option becomes selected because its checked condition evaluates to true.
12. Managing Multiple Fields with One State Object
When a form contains several inputs, you can keep their values in a single object instead of creating a separate state variable for every field.
import { useState } from "react";
function UserForm() {
const [formData, setFormData] = useState({
name: "",
email: "",
phone: ""
});
function handleChange(event) {
const { name, value } = event.target;
setFormData((previousData) => ({
...previousData,
[name]: value
}));
}
return (
<form>
<label htmlFor="name">Name:</label>
<input
id="name"
name="name"
value={formData.name}
onChange={handleChange}
/>
<label htmlFor="email">Email:</label>
<input
id="email"
name="email"
type="email"
value={formData.email}
onChange={handleChange}
/>
<label htmlFor="phone">Phone:</label>
<input
id="phone"
name="phone"
type="tel"
value={formData.phone}
onChange={handleChange}
/>
<pre>{JSON.stringify(formData, null, 2)}</pre>
</form>
);
}
export default UserForm;
Understanding the Code
formData stores all field values in one object.
name identifies which property should be updated.
value contains the latest value of the edited field.
...previousData preserves the values of other fields.
[name]: value updates the matching property dynamically.
JSON.stringify() displays the state object for demonstration purposes.
13. Handling Form Submission
Controlled components make it straightforward to access form values during submission because the values are already stored in React state.
import { useState } from "react";
function LoginForm() {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
function handleSubmit(event) {
event.preventDefault();
console.log("Username:", username);
console.log("Password length:", password.length);
alert("Form submitted!");
}
return (
<form onSubmit={handleSubmit}>
<label htmlFor="username">Username:</label>
<input
id="username"
value={username}
onChange={(event) => setUsername(event.target.value)}
required
/>
<label htmlFor="password">Password:</label>
<input
id="password"
type="password"
value={password}
onChange={(event) => setPassword(event.target.value)}
required
/>
<button type="submit">Login</button>
</form>
);
}
export default LoginForm;
Explanation:
onSubmit runs when the form is submitted.
event.preventDefault() prevents the browser's default form navigation or reload.
username and password contain the current state values.
required enables built-in browser validation for empty fields.
Security note: This example demonstrates form handling only. Do not log actual passwords in a production application. Authentication should use a secure backend.
14. Validation in Controlled Components
Validation checks whether user input satisfies the application's requirements. Since controlled components keep input values in state, those values can be checked before the form is submitted.
Example: Name Validation
import { useState } from "react";
function NameValidationForm() {
const [name, setName] = useState("");
const [error, setError] = useState("");
const [success, setSuccess] = useState("");
function handleSubmit(event) {
event.preventDefault();
if (!name.trim()) {
setError("Name is required.");
setSuccess("");
return;
}
setError("");
setSuccess("Form submitted successfully!");
}
return (
<form onSubmit={handleSubmit} noValidate>
<label htmlFor="name">Name:</label>
<input
id="name"
value={name}
onChange={(event) => setName(event.target.value)}
aria-invalid={Boolean(error)}
aria-describedby={error ? "name-error" : undefined}
/>
{error && <p id="name-error" role="alert">{error}</p>}
{success && <p role="status">{success}</p>}
<button type="submit">Submit</button>
</form>
);
}
export default NameValidationForm;
The noValidate attribute disables the browser's automatic validation interface for this example, allowing the custom React error message to be demonstrated. The application should still implement the validation rules it requires.
15. Resetting Controlled Form Fields
To reset a controlled form, update the React state variables to their initial values. Changing only the DOM is not sufficient because React state controls the displayed values.
import { useState } from "react";
function ResetForm() {
const initialValues = { name: "", email: "" };
const [formData, setFormData] = useState(initialValues);
function handleChange(event) {
const { name, value } = event.target;
setFormData((previous) => ({
...previous,
[name]: value
}));
}
function handleReset() {
setFormData(initialValues);
}
return (
<form onReset={handleReset}>
<input
name="name"
placeholder="Name"
value={formData.name}
onChange={handleChange}
/>
<input
name="email"
type="email"
placeholder="Email"
value={formData.email}
onChange={handleChange}
/>
<button type="reset">Reset Form</button>
</form>
);
}
export default ResetForm;
When the reset button is clicked, the form's reset event calls handleReset(), which restores the controlled fields to empty strings.
16. Controlled vs. Uncontrolled Components
| Feature |
Controlled Component |
Uncontrolled Component |
| Value management |
Managed by React state. |
Managed by the DOM. |
| Common props |
value or checked. |
defaultValue or defaultChecked. |
| Change handling |
Usually uses onChange to update state. |
Can rely on the browser's normal input behavior. |
| Access to current value |
Available through React state. |
Can be read with a ref or FormData. |
| Validation |
Easy to integrate with state-based validation. |
Can use browser validation or custom validation. |
| Programmatic updates |
Update the corresponding state. |
Usually requires DOM access or a form reset. |
| Best suited for |
Dynamic forms, live feedback, and complex interactions. |
Simple forms that need values mainly at submission time. |
17. Common Mistakes in Controlled Components
| Mistake |
Problem |
Correct Approach |
Missing onChange |
The controlled input cannot be edited normally. |
Add an event handler that updates state. |
Using value for a checkbox's selected state |
The checkbox selection is not controlled correctly. |
Use checked. |
Using event.target.value for checkbox selection |
It reads the checkbox's data value, not whether it is checked. |
Use event.target.checked. |
Initializing text state with undefined |
The input may switch from uncontrolled to controlled. |
Initialize text state with "". |
Updating state asynchronously in onChange |
The displayed value may revert or the cursor may behave unexpectedly. |
Update the controlled value synchronously. |
| Replacing the entire state object accidentally |
Other input values may be lost. |
Preserve existing properties with the spread operator. |
| Using an incorrect button type |
A button may submit a form unexpectedly. |
Use type="button" for ordinary actions and type="submit" for submission. |
18. Best Practices for Controlled Components
- Use
useState to store input values when the UI needs to react to changes.
- Provide an
onChange handler whenever an editable input is controlled by value or checked.
- Initialize text values with strings and checkbox values with booleans.
- Update controlled input state synchronously inside the change handler.
- Use meaningful state names, such as
email, username, and isAccepted.
- Use the functional state-update form when the new object depends on previous state.
- Use labels and accessible error messages.
- Validate data before submission and on the server when security or data integrity matters.
- Avoid storing sensitive information unnecessarily or printing it in logs.
- For large forms, consider separating fields into smaller components to improve maintainability and rendering performance.
19. Performance Considerations
Controlled inputs usually update React state on each change, which causes the component that owns the state to render again. For most forms, this behavior is expected and performs well.
- Keep form state in the smallest component that needs it.
- Avoid placing frequently updated form state at the top of a very large component tree unless necessary.
- Separate unrelated page content from the component that handles frequent input changes.
- Use memoization or deferred rendering only when profiling shows a real performance problem.
Example: If a search input updates on every keystroke, keep its state close to the search component rather than making the entire application depend on that state unnecessarily.
20. Complete Mini Project: Controlled Student Registration Form
This mini project combines controlled text inputs, a dropdown, a checkbox, validation, submission handling, and success messages.
import { useState } from "react";
function StudentRegistration() {
const initialForm = {
name: "",
email: "",
course: "",
acceptedTerms: false
};
const [form, setForm] = useState(initialForm);
const [errors, setErrors] = useState({});
const [message, setMessage] = useState("");
function handleChange(event) {
const { name, value, checked, type } = event.target;
setForm((previous) => ({
...previous,
[name]: type === "checkbox" ? checked : value
}));
setErrors((previous) => ({
...previous,
[name]: ""
}));
setMessage("");
}
function handleSubmit(event) {
event.preventDefault();
const nextErrors = {};
if (!form.name.trim()) {
nextErrors.name = "Please enter your name.";
}
if (!form.email.trim()) {
nextErrors.email = "Please enter your email.";
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.email)) {
nextErrors.email = "Please enter a valid email address.";
}
if (!form.course) {
nextErrors.course = "Please select a course.";
}
if (!form.acceptedTerms) {
nextErrors.acceptedTerms = "Please accept the terms.";
}
setErrors(nextErrors);
if (Object.keys(nextErrors).length === 0) {
setMessage("Registration submitted successfully!");
console.log("Registration details:", {
name: form.name,
email: form.email,
course: form.course,
acceptedTerms: form.acceptedTerms
});
} else {
setMessage("Please correct the errors below.");
}
}
function handleReset() {
setForm(initialForm);
setErrors({});
setMessage("");
}
return (
<form onSubmit={handleSubmit} onReset={handleReset} noValidate>
<h2>Student Registration</h2>
<label htmlFor="name">Full Name:</label>
<input
id="name"
name="name"
value={form.name}
onChange={handleChange}
aria-invalid={Boolean(errors.name)}
/>
{errors.name && <p role="alert">{errors.name}</p>}
<label htmlFor="email">Email Address:</label>
<input
id="email"
name="email"
type="email"
value={form.email}
onChange={handleChange}
aria-invalid={Boolean(errors.email)}
/>
{errors.email && <p role="alert">{errors.email}</p>}
<label htmlFor="course">Select Course:</label>
<select
id="course"
name="course"
value={form.course}
onChange={handleChange}
aria-invalid={Boolean(errors.course)}
>
<option value="">Choose a course</option>
<option value="react">React JS</option>
<option value="flutter">Flutter</option>
<option value="figma">Figma UI/UX</option>
</select>
{errors.course && <p role="alert">{errors.course}</p>}
<label>
<input
type="checkbox"
name="acceptedTerms"
checked={form.acceptedTerms}
onChange={handleChange}
/>
I accept the terms and conditions.
</label>
{errors.acceptedTerms && (
<p role="alert">{errors.acceptedTerms}</p>
)}
{message && <p role="status">{message}</p>}
<button type="submit">Register</button>
<button type="reset">Reset Form</button>
</form>
);
}
export default StudentRegistration;
Project Workflow
- The user enters their name and email address.
- The user selects a course from the dropdown.
- The user accepts the terms and conditions.
- The user clicks the Register button.
- React validates the entered information.
- If errors exist, React displays messages next to the relevant fields.
- If all checks pass, the form displays a success message.
- The Reset Form button restores the initial state.
Note: This is a front-end demonstration. It does not save registration data to a database. A real application should send the data to a secure backend and validate it on the server.
21. Interview Questions and Answers
Q1. What is a controlled component in React?
A controlled component is a form element whose current value or selected state is managed by React state.
Q2. Why are controlled components useful?
They make it easier to validate input, display dynamic content, update values programmatically, and manage form submission.
Q3. Which Hook is commonly used for controlled components?
The useState Hook is commonly used to store and update form field values in functional components.
Q4. What is the role of the value prop?
The value prop controls the current value of text inputs, textareas, and select elements.
Q5. What is the role of the checked prop?
The checked prop controls whether a checkbox or radio button is selected.
Q6. Why is onChange important?
The onChange handler reads user input and updates React state. A controlled input normally needs this handler to remain editable.
Q7. What is the difference between value and defaultValue?
value controls the current value through React, while defaultValue specifies the initial value of an uncontrolled input.
Q8. Can checkboxes be controlled components?
Yes. Use checked to represent the selected state and onChange to update the state.
Q9. How do you manage multiple inputs with one handler?
Give each input a unique name attribute and use event.target.name and event.target.value to update the matching property in a state object.
Q10. What happens if a controlled input has value but no onChange?
The input behaves as read-only because React keeps forcing the specified value. Add an onChange handler or use readOnly if the field is intentionally non-editable.
22. Practice Exercises
- Create a controlled input that displays the user's name as they type.
- Build a controlled email field with email-format validation.
- Create a password input and display its character count.
- Build a controlled textarea for collecting feedback.
- Create a dropdown for selecting a training course.
- Build a checkbox for accepting terms and conditions.
- Create radio buttons for choosing online or classroom learning.
- Manage name, email, and phone fields with one state object.
- Build a controlled login form with submission handling.
- Create a registration form with validation, error messages, and a reset button.
23. Quick Revision Checklist
- Understand the definition of controlled components.
- Know how React state controls form values.
- Use
useState to manage input values.
- Use
value for text inputs, textareas, and dropdowns.
- Use
checked for checkboxes and radio buttons.
- Handle input changes with
onChange.
- Manage multiple fields using one state object.
- Validate input before submission.
- Handle form submission with
onSubmit.
- Reset controlled values by updating React state.
- Keep controlled values initialized to appropriate types.
- Understand the difference between controlled and uncontrolled components.
24. Official Learning Resources
25. Conclusion
Controlled components are an important part of form handling in React JS. They allow React state to manage user input and provide a predictable way to validate, update, reset, and submit form data. By practicing text inputs, checkboxes, radio buttons, dropdowns, and multi-field forms, you can build interactive and maintainable React applications.
Explore the Course: JustAcademy React JS Training Course | Register for React JS Course Demo